<?php

namespace Biz\service\adCalendar;


use Biz\cache\advertisement\CAdCounter;
use Biz\model\advertisement\MHhzAdmin;
use Biz\model\advertisement\MHhzDepartment;
use Biz\model\advertisement\MHhzPositionPreview;
use Biz\model\advertisement\MPosition;
use Biz\model\advertisement\MPositionAdRule;
use Biz\model\advertisement\MPositionAdType;
use Biz\model\advertisement\MPositionBaseInfo;
use Biz\model\advertisement\MPositionProject;
use Biz\model\advertisement\MPositionProjectRelations;
use Biz\service\advertisement\AdMsgManagement;
use Doraemon\tools\Log;
use Doraemon\tools\WorkWeChat;
use Exception;
use util\UserHelper;

class AdPosition extends AdPositionData
{
    protected static string $defaultDate = '0000-00-00 00:00:00';
    protected static bool $admin_op = false;

    use AdMsgManagement;


    public static function formatOccupied($uid)
    {
        $time = self::occupied($uid);
        if (empty($time)) return '高优占位';
        list($now, $next) = $time;
        $fn = fn ($time) => date("Y年m月d日 H:i:s", $time);
        $now = array_map($fn, $now);
        $next = array_map($fn, $next);
        return compact('now', 'next');
    }

    public static function formatStartDay($time)
    {
        return strtotime(date("Ymd", $time));
    }

    /**
     * 根据用户id查询占位周期
     * @param $uid
     * @return array
     */
    public static function occupied($uid): array
    {
        $admin = self::adminInfo($uid);
        $response = [];
        if ($admin->department->occupied > 0) {
            $now = time() - 11 * 3600;
            //如果开启了周期,根据开放日计算占位周期
            $start = self::getStartDay(false);
            $day = date('N', $now);
            if ($start == 0) { //每天开放
                $circle = strtotime(date("Ymd", strtotime($admin->department->occupied . "day")));
                $response= [
                    [strtotime(date("Ymd")),  $circle - 1],
                    [$circle, strtotime(date("Ymd", strtotime($admin->department->occupied + 1 . "day"))) - 1],
                ];
            } else {//上个周期还没完
                //起始时间上次开放日 ~ 周期
                $startDate = $start > $day ? $now - (7 - $start + $day) * 86400 : $now - ($day - $start) * 86400;
                $startDate = self::formatStartDay($startDate);
                $nextStartDate = $startDate + $admin->department->occupied * 86400;
                $response= [
                    [strtotime(date("Ymd")), $nextStartDate - 1],
                    [$nextStartDate, $nextStartDate + 7 * 86400 - 1],
                ];
            }
        }

        return $response;
    }

    /**
     * 项目详情
     * @param $id
     * @return array
     * @throws Exception
     */
    public static function projectInfo($id)
    {
        $info = self::getProjectInfo($id);
        $admin = MHhzAdmin::get('username', ['id' => $info['admin_id'] ?? 0]);
        $info['admin'] = $admin ?: '';
        //查询项目中所有的占位
        $where = [
            'GROUP' => [MPositionProjectRelations::_TABLE_NAME . '.id'],
            //"OR #the first condition" => [
            //    Preview::_TABLE_NAME . '.is_deleted #the first condition' => Preview::IS_DELETED_NO,
            //    Preview::_TABLE_NAME . '.is_deleted #the second condition' => null,
            //],
            MPositionProjectRelations::_TABLE_NAME . '.project_id' => $id,
        ];
        $config = MPositionProjectRelations::select(
            [
                //"[>]" . Preview::_TABLE_NAME => ["id" => "project_relation_id"],
                "[>]" . MPosition::_TABLE_NAME => ['ad_id' => 'id'],
            ],
            [
                MPositionProjectRelations::_TABLE_NAME . '.id',
                'start_show_time',
                'end_show_time',
                'ad_id',
                'is_share',
                'ad_unique',
                'project_status',
                'material_id',
                'is_exclusive'
                //'preview_end_time' => Medoo::raw(distinct(Preview::_TABLE_NAME) . '.end_time'),
            ],
            $where
        );

        //物料设置数量
        $relIds = array_column($config, 'id');
        $nums = MPositionBaseInfo::getCountByRelIds($relIds);
        $previewEndTimes = MHhzPositionPreview::getEndTimeByPositionRelationIds($relIds);
        $list = [];

        $dataListMap = [];

        foreach ($config as $v) {
            if ($v['project_status'] == MPositionProject::PROJECT_STATUS_OK) {
                $position_config = MPositionAdType::get('position_config', ['id' => $v['ad_id']]);
                $position_config = json_decode($position_config, true);
                $dataListMap[$v['ad_id']][] = [
                    'id' => $v['id'],
                    'start_date' => $v['start_show_time'],
                    'end_date' => $v['end_show_time'],
                    'is_share' => $v['is_share'],
                    'unique' => $v['ad_unique'],
                    'route' => \Doraemon\service\position\PositionRoute::generateAdRoute($position_config, $v['id']),
                    'is_preview' => $previewEndTimes[$v['id']] && strtotime($previewEndTimes[$v['id']]) > time(),
                    'preview_end_time' => $previewEndTimes[$v['id']] ?: '',
                    'nums' => $nums[$v['id']] ?? 0,
                    'material_id' => $v['material_id'] ?? '',
                    'is_exclusive' => $v['is_exclusive'] ?? MPositionProjectRelations::NOT_EXCLUSIVE,
                    'show_material_id' => in_array($v['ad_id'], [15,16,17,19,21])
                ];
            }
        }

        foreach ($dataListMap as $k => $v) {
            $list[] = [
                'ad_id' => strval($k),
                'data_list' => $v,
                'occupied_time' => AdPosition::occupied($info['admin_id'])
            ];
        }

        return compact('info', 'list');
    }

    private function getFriezaDomain()
    {
        $proto = "https://";
        if ($_SERVER['HTTP_X_FORWARDED_PROTO']) {
            $proto = $_SERVER['HTTP_X_FORWARDED_PROTO'];
        }
        if ($_SERVER['HTTP_X_FORWARDED_HOST']) {
            return $proto . "://" . str_replace("puckapi", "frieza", $_SERVER['HTTP_X_FORWARDED_HOST']);
        }
        return $proto . "://" . str_replace("puckapi", "frieza", $_SERVER['HTTP_HOST']);
    }

    /**
     * 项目列表
     * @param $department_id
     * @param $name
     * @param $status
     * @param $page
     * @return array
     */
    public static function projectList($admin, $start_time, $end_time, $department_id, $name, $status, $page)
    {
        //管理员昵称
        $adminCondition = [];
        if ($admin) {
            $admin_id = MHhzAdmin::get('id', ['username' => $admin]);
            $adminCondition['admin_id'] = $admin_id ?: 0;
        }

        //项目名称解析
        $departmentCondition = [];
        if ($department_id) {
            $departmentCondition = [
                'department_id' => $department_id,
            ];
        }

        //项目名称解析
        $nameCondition = [];
        if (strlen($name)) {
            $nameCondition = [
                'project_name[~]' => $name,
            ];
        }
        //状态解析
        $timeCondition = [];
        if ($status > 0) {
            switch ($status) {
                case 2://未开始
                    $timeCondition = [
                        'start_show_time[>]' => date("Y-m-d H:i:s"),
                    ];
                    break;
                case 3://进行中
                    $timeCondition = [
                        'start_show_time[<]' => date("Y-m-d H:i:s"),
                        'end_show_time[>]' => date("Y-m-d H:i:s"),
                    ];
                    break;
            }
        }

        //时间
        if ($start_time) {
            $timeCondition['start_show_time[>=]'] = $start_time;
        }
        if ($end_time) {
            $timeCondition['start_show_time[<=]'] = $end_time;
        }

        $condition = [
                'project_status' => MPositionProject::PROJECT_STATUS_OK,
            ] + $timeCondition + $nameCondition + $departmentCondition + $adminCondition;

        $total = MPositionProject::count($condition);
        $size = 20;
        $offset = ($page - 1) * $size;

        $condition = $condition + [
                'ORDER' => ['id' => 'DESC'],
                'LIMIT' => [$offset, $size],
            ];

        $dbList = MPositionProject::select('*', $condition);

        $list = [];
        //格式化列表
        if ($dbList) {
            $nowDate = date("Y-m-d H:i:s");
            $department_id = array_column($dbList, 'department_id');
            $admin_id = array_column($dbList, 'admin_id');
            //批量查询信息
            $department = MHhzDepartment::select('*', ['id' => $department_id]);
            $department = empty($department) ? [] : array_column($department, 'name', 'id');
            $admin = MHhzAdmin::select('*', ['id' => $admin_id]);
            $admin = empty($admin) ? [] : array_column($admin, 'username', 'id');

            foreach ($dbList as $v) {
                if ($v['end_show_time'] > $nowDate) {
                    $show_desc = $v['start_show_time'] < $nowDate ? '进行中' : '未开始';
                } else {
                    $show_desc = '已下线';
                }
                $t = md5($v['id']. "ctr");
                $projectAdminInfo = MHhzAdmin::getById($v['admin_id']);
                $list[] = [
                    'id' => $v['id'],
                    'project_name' => $v['project_name'],
                    'start_date' => $v['start_show_time'] == self::$defaultDate ? '-' : $v['start_show_time'],
                    'end_date' => $v['end_show_time'] == self::$defaultDate ? '-' : $v['end_show_time'],
                    'status_desc' => $show_desc,
                    'admin' => $admin[$v['admin_id']] ?? '',
                    'department' => $department[$v['department_id']] ?? '',
                    'ctr_down_url' => UserHelper::isSuperAdmin() || UserHelper::getAdminId() == $v['admin_id'] ||
                        ($projectAdminInfo && UserHelper::getDepartmentId() == 13 && $projectAdminInfo['department_id'] == 2)
                        ? "/index/adProject/ctrdownload?project_id={$v['id']}&sign={$t}" : ""
                ];
            }
        }

        return compact('total', 'list');
    }

    public static function projectListToAdmin($admin, $start_time, $end_time, $department_id, $name, $status, $page, $size = 20)
    {
        //管理员昵称
        $adminCondition = [];
        if ($admin) {
            $admin_id = MHhzAdmin::select('id', ['username' => $admin]);
            $adminCondition['admin_id'] = $admin_id ?: 0;
        }

        //项目名称解析
        $departmentCondition = [];
        if ($department_id) {
            $departmentCondition = [
                'department_id' => $department_id,
            ];
        }

        //项目名称解析
        $nameCondition = [];
        if (strlen($name)) {
            $nameCondition = [
                'project_name[~]' => $name,
            ];
        }
        //状态解析
        $timeCondition = [];
        if ($status > 0) {
            switch ($status) {
                case 2://未开始
                    $timeCondition = [
                        'start_show_time[>]' => date("Y-m-d H:i:s"),
                    ];
                    break;
                case 3://进行中
                    $timeCondition = [
                        'start_show_time[<]' => date("Y-m-d H:i:s"),
                        'end_show_time[>]' => date("Y-m-d H:i:s"),
                    ];
                    break;
            }
        }

        //时间
        if ($start_time) {
            $timeCondition['start_show_time[>=]'] = $start_time;
        }
        if ($end_time) {
            $timeCondition['start_show_time[<=]'] = $end_time;
        }

        $condition = [
                'project_status' => MPositionProject::PROJECT_STATUS_OK,
            ] + $timeCondition + $nameCondition + $departmentCondition + $adminCondition;

        $total = MPositionProject::count($condition);
        $offset = ($page - 1) * $size;

        $condition = $condition + [
                'ORDER' => ['id' => 'DESC'],
                'LIMIT' => [$offset, $size],
            ];

        $dbList = MPositionProject::select('*', $condition);

        $list = [];
        //格式化列表
        if ($dbList) {
            $nowDate = date("Y-m-d H:i:s");
            $department_id = array_column($dbList, 'department_id');
            $admin_id = array_column($dbList, 'admin_id');
            //批量查询信息
            $department = MHhzDepartment::select('*', ['id' => $department_id]);
            $department = empty($department) ? [] : array_column($department, 'name', 'id');
            $admin = MHhzAdmin::select('*', ['id' => $admin_id]);
            $admin = empty($admin) ? [] : array_column($admin, 'username', 'id');

            foreach ($dbList as $v) {
                if ($v['end_show_time'] > $nowDate) {
                    $show_desc = $v['start_show_time'] < $nowDate ? '进行中' : '未开始';
                } else {
                    $show_desc = '已下线';
                }
                $t = md5($v['id']. "ctr");
                $projectAdminInfo = MHhzAdmin::getById($v['admin_id']);
                $list[] = [
                    'id' => $v['id'],
                    'project_name' => $v['project_name'],
                    'start_date' => $v['start_show_time'] == self::$defaultDate ? '-' : $v['start_show_time'],
                    'end_date' => $v['end_show_time'] == self::$defaultDate ? '-' : $v['end_show_time'],
                    'status_desc' => $show_desc,
                    'admin' => $admin[$v['admin_id']] ?? '',
                    'department' => $department[$v['department_id']] ?? '',
                ];
            }
        }

        return compact('total', 'list');
    }


    /**
     * 释放项目占位
     */
    public static function releaseProject($id, $reason, $admin_id)
    {
        //查询资源位项目
        $project = self::getProjectInfo($id);

        //权限验证
        if (!self::author($admin_id, $project)) {
            throw new Exception('权限不足');
        }

        //释放项目
        $data = [
            'project_status' => MPositionProject::PROJECT_STATUS_RELEASE,
            'release_reason' => $reason,
        ];
        MPositionProject::update($data, ['id' => $id]);

        //查询项目中所有的占位
        $config = MPositionProjectRelations::select('*', ['project_id' => $id]);

        //释放占位
        $ids = [];
        if ($config) {
            foreach ($config as $v) {
                $ids[] = $v['id'];
                $data = [
                    'release_reason' => $reason,
                    'project_status' => MPositionProjectRelations::PROJECT_STATUS_RELEASE,
                ];
                MPositionProjectRelations::update($data,
                    ['id' => $v['id'], 'project_status' => MPositionProjectRelations::PROJECT_STATUS_OK]);
            }
        }

        //通知记录
        self::sendMsg(self::MSG_TYPE_RELEASE_PROJECT, [
            'project_name' => $project['project_name'],
            'reason' => $reason,
        ]);

        //释放占位的物料配置
        \Doraemon\service\position\AdmOperationModule::releasePosition($ids);
    }

    /**
     * 查询项目信息
     * @param $project_id
     * @return array|mixed
     * @throws Exception
     */
    public static function getProjectInfo($project_id)
    {
        $project = MPositionProject::get('*', ['id' => $project_id]);

        if (!$project) throw new Exception('项目信息不存在');

        if ($project['project_status'] == MPositionProject::PROJECT_STATUS_RELEASE) throw new Exception('项目已经释放');

        $projectAdminInfo = MHhzAdmin::getById($project['admin_id']);
        $isSuperAdmin = UserHelper::isSuperAdmin();
        $project['can_edit'] = $isSuperAdmin || UserHelper::getAdminId() == $project['admin_id'] || ($projectAdminInfo && (UserHelper::getDepartmentId() == 18) && $project['department_id'] == 18);
        $project['can_upload'] = $isSuperAdmin || UserHelper::getAdminId() == $project['admin_id'] || ($projectAdminInfo && in_array(UserHelper::getDepartmentId(), [13, 18]) && $project['department_id'] == 2) || ($projectAdminInfo && (UserHelper::getDepartmentId() == 18) && $project['department_id'] == 18);
        return $project;
    }

    /**
     * 修改权限,释放权限判断
     * @param $admin_id
     * @param $project
     * @return bool
     */
    public static function author($admin_id, $project)
    {
        $admin = MHhzAdmin::get('*', ['id' => $admin_id]);

        if ($admin['is_super_admin'] == MHhzAdmin::IS_SUPER_ADMIN_YES) {
            self::$admin_op = true;
            return true;
        }

        if ($admin_id == $project['admin_id']) {
            return true;
        }

        $manager_id = MHhzDepartment::get('manager_id', ['id' => $project['department_id']]);

        if ($manager_id == $admin_id) {
            self::$admin_op = true;
            return true;
        }

        if ($project['department_id'] == 18 && $project['department_id'] == $admin['department_id']) {
            return true;
        }

        return false;
    }

    /**
     * 释放项目中的一个占位
     */
    public static function releaseOne($id, $reason, $admin_id)
    {
        //查询占位的信息
        $pos = MPositionProjectRelations::get('*', ['id' => $id]);
        if (!$pos) {
            throw new Exception('占位信息不存在');
        }
        if ($pos['project_status'] == MPositionProjectRelations::PROJECT_STATUS_RELEASE) {
            throw new Exception('占位信息已经释放');
        }

        $project_id = $pos['project_id'] ?? 0;

        //查询资源位项目
        $project = self::getProjectInfo($project_id);

        //权限验证
        if (!self::author($admin_id, $project)) {
            throw new Exception('权限不足');
        }

        //查询项目中所有的占位
        $config = MPositionProjectRelations::select('*', ['project_id' => $project_id]);

        //修改占位状态
        $data = [
            'release_reason' => $reason,
            'project_status' => MPositionProjectRelations::PROJECT_STATUS_RELEASE,
        ];
        MPositionProjectRelations::update($data, ['id' => $id]);

        $ad_id = MPositionProjectRelations::get('ad_id', ['id' => $id]);

        $adNameMap = self::positionAdTypeOptions();

        //群消息发送
        self::sendMsg(self::MSG_TYPE_RELEASE_ONE, [
            'project_name' => $project['project_name'],
            'ad_name' => $adNameMap[$ad_id] ?? '-',
            'reason' => $reason,
            'start_show_time' => $pos['start_show_time'],
            'end_show_time' => $pos['end_show_time'],
        ]);

        //项目时间处理
        $arr = $releaseArr = [];
        foreach ($config as $v) {
            if ($v['project_status'] == MPositionProject::PROJECT_STATUS_OK && $v['id'] != $id) {
                //正常配置
                $arr[] = $v;
            } else {
                //释放配置
                $releaseArr[] = $v;
            }
        }

        if (empty($arr) && empty($releaseArr)) {
            return;
        }

        //修改项目状态
        $start_show_time = min(array_column($arr ?: $releaseArr, 'start_show_time'));
        $end_show_time = max(array_column($arr ?: $releaseArr, 'end_show_time'));
        $data = [
            'start_show_time' => $start_show_time,
            'end_show_time' => $end_show_time,
        ];
        if (empty($arr)) {
            $data['project_status'] = MPositionProject::PROJECT_STATUS_RELEASE;
        }

        MPositionProject::update($data, ['id' => $project_id]);

        //释放占位的物料配置
        \Doraemon\service\position\AdmOperationModule::releasePosition($id);
    }

    /**
     * 创建资源位占位
     * [
     *      ['2020-11-01', '2020-11-01', '1', 0, 1, 9],
     *      [start_date, end_date, index, id, share, ad_id],
     * ]
     * @param     $dateRangeArr
     * @return array
     */
    public static function dateRangeConflict($dateRangeArr)
    {
        //把时间段进行排序,时间轴上从小到大排列
        sort($dateRangeArr);

        foreach ($dateRangeArr as $k => $v) {
            $i = $k + 1;
            //较小时间段的结束日期 > 较大时间段的开始日期大,那么日期有冲突
            if (isset($dateRangeArr[$i]) && $v[1] > $dateRangeArr[$i][0]) {
                return [$v, $dateRangeArr[$i]];
            }
        }
    }

    /**
     * @throws Exception
     */
    public static function createPositionProcessCommonCheck($config): array
    {
        if (!$config) {
            throw new Exception('未配置资源位');
        }

        if (is_string($config)) {
            $config = json_decode($config, true);
        }

        $config = is_array($config) ? $config : [];

        $ad_ids = [];
        foreach ($config as $item) {
            $ad_id = $item['ad_id'] ?? 0;
            $data_list = $item['data_list'] ?? [];
            //提交的占位时间和参数校验
            foreach ($data_list as $k => $v) {
                if (!$v['is_share']) {
                    throw new Exception('资源位参数[共享]未选择');
                }
                if (!($ad_id && $v['start_date'] && $v['end_date'])) {
                    throw new Exception('资源位参数不全');
                }
                if ($v['start_date'] > $v['end_date']) {
                    throw new Exception('时间范围不正确');
                }
                $ad_ids[$ad_id] = $ad_id;
            }
        }
        $ad_ids = array_values($ad_ids);
        return [$config, $ad_ids];
    }

    /**
     * 创建资源位占位
     * @param     $name
     * @param     $desc
     * @param     $department_id
     * @param     $admin_id
     * @param     $config
     * @param numeric $id
     * @param numeric $is_sold
     * @throws Exception
     */
    public static function createPositionProcess($name, $desc, $department_id, $admin_id, $config, $id = 0 , $is_sold = MPositionProject::SOLD_STATUS)
    {
        // 避免判断冲突
        if (!CAdCounter::lock()) throw new Exception('稍后再试');

        try {
            MPositionProject::action(function () use ($name, $desc, $department_id, $admin_id, $config, $id, $is_sold) {
                //创建项目
                $project_id = self::createPositionProject($name, $desc, $department_id, $admin_id, $id,$is_sold);
                $admin = MHhzAdmin::get('*', ['id' => $admin_id]);
                $department_id = $admin['department_id'];
                //校验config参数
                list($config, $ad_ids) = self::createPositionProcessCommonCheck($config);

                //取资源位类型对应的规则
                $rules = AdPositionRules::getRulesByAdType($ad_ids);
                //静态名称资源位
                $adNameMap = self::positionAdTypeOptions();
                $conflictAdIds = $ad_ids;
                $unsharedTimeRange = [];
                $allTimeRange = [];
                $allUniTimeRange = [];
                foreach ($config as $item) {
                    $ad_id = $item['ad_id'] ?? 0;
                    $data_list = $item['data_list'] ?? [];
                    $tempRules = $rules[$ad_id] ?? [];
                    //获取[互斥关键词][互斥资源位]规则
                    $uniqueRules = $tempRules[MPositionAdRule::RULE_TYPE_UNIQUE] ?? [];
                    $tempConflictRules = $tempRules[MPositionAdRule::RULE_TYPE_CONFLICT] ?? [];

                    foreach ($tempConflictRules as $tempConflictRule) {
                        $conflictAdIds = array_values(array_unique(array_merge($conflictAdIds, json_decode($tempConflictRule))));
                    }

                    foreach ($data_list as $k => $v) {
                        if ($uniqueRules && empty($v['unique'])) throw new Exception('互斥关键词未填写');
                        $row = [
                            $v['start_date'],
                            $v['end_date'],
                            $k + 1,
                            $v['id'],
                            $v['is_share'],
                            $ad_id,
                            $project_id,
                        ];

                        $allTimeRange[$ad_id][] = $row;
                        $uniqueArr = $uniqueRules ? explode(',', $v['unique']) : [$v['unique']];
                        foreach ($uniqueArr as $unique) {
                            $uni_ad_id = $uniqueRules ? $ad_id . "_" . $unique : $ad_id;
                            if ($v['is_share'] == MPositionProjectRelations::SHARE_NO) $unsharedTimeRange[$uni_ad_id][] = $row;
                            $allUniTimeRange[$uni_ad_id][] = $row;
                        }
                    }
                }

                //检查不共享的配置,是否有冲突
                foreach ($unsharedTimeRange as $uni_ad_id => $rows) {
                    $ad_id = current(explode('_', $uni_ad_id));
                    if (self::dateRangeConflict($rows)) {
                        throw new Exception($adNameMap[$ad_id] . ' 输入占位配置时间冲突');
                    }
                }

                //查询相同位置的占位的情况
                $conflictExists = empty($conflictAdIds) ? [] : MPositionProjectRelations::select('*', [
                    'ad_id' => $conflictAdIds,
                    'project_status' => MPositionProjectRelations::PROJECT_STATUS_OK,
                    'end_show_time[>]' => date('Y-m-d H:i:s'),
                ]);
                $exist_map = empty($conflictExists) ? [] : array_column($conflictExists, null, 'id');
                $exists = [];
                foreach ($conflictExists as $v) {
                    if (in_array($v['ad_id'], $ad_ids)) $exists[] = $v;
                }

                //检查占位时间冲突
                //输入共享与存在的不共享检查冲突;输入不共享与存在的所有检查冲突
                foreach ($exists as $v) {
                    $id = $v['id'];
                    $ad_id = $v['ad_id'];
                    $tempRules = $rules[$ad_id] ?? [];
                    $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_UNIQUE] ?? [];
                    $uniqueArr = $tempRules ? explode(',', $v['ad_unique']) : [$v['ad_unique']];
                    $msg = $tempRules ? "提交失败，【{$adNameMap[$ad_id]}】在【%s】【%s】时间冲突,冲突关键词:%s" : "提交失败，【{$adNameMap[$ad_id]}】在【%s】【%s】时间冲突";
                    $row = [
                        $v['start_show_time'],
                        $v['end_show_time'],
                        $v['id'],
                        $v['id'],
                        $v['is_share'],
                        $ad_id,
                        $v['project_id'],
                    ];

                    foreach ($uniqueArr as $unique) {
                        $uni_ad_id = $tempRules ? $ad_id . "_" . $unique : $ad_id;
                        $temp = $v['is_share'] == MPositionProjectRelations::SHARE_OK ? ($unsharedTimeRange[$uni_ad_id] ?? []) : ($allUniTimeRange[$uni_ad_id] ?? []);
                        $ids = array_column($temp, 3);
                        if (in_array($v['id'], $ids)) continue;
                        $temp[] = $row;

                        if ($conflict = self::dateRangeConflict($temp)) {
//                            print_r($temp);
                            $start = $conflict[0];
                            $end = $conflict[1];
                            $err = $tempRules ? vsprintf($msg, [$end[0], $end[1], $unique]) : vsprintf($msg, [$end[0], $end[1]]);
                            $err2 = $tempRules ? vsprintf($msg, [$start[0], $start[1], $unique]) : vsprintf($msg, [$start[0], $start[1]]);
                            if ($start[3] == $id) throw new Exception($err);
                            if ($end[3] == $id) throw new Exception($err2);
                        }
                    }
                }

                //取资源位类型对应的规则
                $rules = AdPositionRules::getRulesByAdType($conflictAdIds);

                //检查资源位共享限制
                AdPositionRules::checkShareOnly($allTimeRange, $rules, $adNameMap);

                //检查使用部门数量限制,需要已经存在的占位,资源位的占位规则,和部门
                AdPositionRules::checkBindLimit($project_id, $exists, $allTimeRange, $department_id, $rules, $adNameMap);

                //检查共享数量限制
                AdPositionRules::checkShareLimit($project_id, $exists, $rules, $allTimeRange, $adNameMap);

                //检查资源位同期占位互斥
                AdPositionRules::checkTimeConflict($project_id, $conflictExists, $rules, $allTimeRange, $adNameMap);

                //创建占位db记录
                $start_show_time = 0;
                $end_show_time = 0;
                $relation_ids = [];
                foreach ($config as $item) {
                    $ad_id = $item['ad_id'] ?? 0;
                    $data_list = $item['data_list'] ?? [];
                    foreach ($data_list as $k => $v) {
                        $start_show_time = $start_show_time ? min(strtotime($v['start_date']), $start_show_time) : strtotime($v['start_date']);
                        $end_show_time = $end_show_time ? max(strtotime($v['end_date']), $end_show_time) : strtotime($v['end_date']);
                        self::createProjectRelations($v['id'], $v['is_share'], $ad_id, $v['unique'] ?? '',
                            $v['start_date'], $v['end_date'], $department_id, $project_id,$v['material_id'] ?? '' , $v['is_exclusive'] ?? MPositionProjectRelations::NOT_EXCLUSIVE);
                        //如果修改关键字段,需要失效资源位
                        $exist_item = $exist_map[$v['id']] ?? [];
                        if (!$exist_item) continue;
                        $modify = $exist_item['is_share'] != $v['is_share'];
                        $modify = $modify || ($exist_item['start_show_time'] != $v['start_date']);
                        $modify = $modify || ($exist_item['ad_id'] != $ad_id);
                        $modify = $modify || ($exist_item['end_show_time'] != $v['end_date']);
                        if ($modify) $relation_ids[] = $v['id'];
                    }
                }

                \Doraemon\service\position\AdmOperationModule::releasePosition($relation_ids);
                //更新项目时间跨度
                if ($start_show_time && $end_show_time) {
                    MPositionProject::update([
                        'start_show_time' => date("Y-m-d H:i:s", $start_show_time),
                        'end_show_time' => date("Y-m-d H:i:s", $end_show_time),
                    ], ['id' => $project_id]);
                }
            });
        } catch (\Exception $e) {
            CAdCounter::lockRelease();
            throw $e;
        }
        CAdCounter::lockRelease();
    }

    /**
     * @throws Exception
     */
    public static function createPositionProject($name, $desc, $department_id, $admin_id, $id = 0, $is_sold = MPositionProject::SOLD_STATUS)
    {
        //项目名称唯一检查
        $checkCondition = ['project_name' => $name, 'project_status' => MPositionProject::PROJECT_STATUS_OK];

        if ($id > 0) $checkCondition['id[!]'] = $id;

        $admin = MHhzAdmin::get('*', ['id' => $admin_id]);

        if ($id > 0) {
            //查询资源位项目
            $project = self::getProjectInfo($id);

            //权限验证
            if (!self::author($admin_id, $project)) {
                throw new Exception('权限不足');
            }
        }

        $count = MPositionProject::count($checkCondition);

        if ($count) throw new Exception('项目名称重复');

        if (!$name) throw new Exception('项目名称为空');

        if (!$admin_id) throw new Exception('未登录');


        if ($id > 0) {
            $data = [
                'project_name' => $name,
                'project_desc' => $desc,
                'admin_id' => $admin_id,
                'is_sold' => $is_sold,
                'edittime' => date("Y-m-d H:i:s"),
            ];
            // 管理员操作, 不更改项目所有者
            if (self::$admin_op) unset($data['admin_id']);
            if ($department_id) $data['department_id'] = $department_id;

            MPositionProject::update($data, ['id' => $id]);
        } else {
            $department_id = $admin['department_id'];
            $data = [
                'project_name' => $name,
                'project_desc' => $desc,
                'department_id' => $department_id,
                'admin_id' => $admin_id,
                'is_sold' => $is_sold,
                'addtime' => date("Y-m-d H:i:s"),
                'preview_user_id' => '',
            ];

            MPositionProject::insert($data);

            $id = MPositionProject::id();

            if (!$id) throw new Exception('创建项目失败,数据库执行失败');
        }

        return $id;
    }


    const MSG_TYPE_RELEASE_ONE = 1;
    const MSG_TYPE_RELEASE_PROJECT = 2;
    const MSG_TYPE_CONFIG_WARNING = 3;


    public static function sendMsg($msgType, $params)
    {
        switch ($msgType) {
            case self::MSG_TYPE_RELEASE_ONE:
                $projectName = $params['project_name'] ?? '';
                $reason = $params['reason'] ?? '';
                $ad_name = $params['ad_name'] ?? '';
                $start_show_time = $params['start_show_time'] ?? '';
                $end_show_time = $params['end_show_time'] ?? '';
                self::positionDing($projectName, $ad_name, $start_show_time, $end_show_time, $reason);
                break;
            case self::MSG_TYPE_RELEASE_PROJECT:
                $projectName = $params['project_name'] ?? '';
                $reason = $params['reason'] ?? '';
                self::projectDing($projectName, $reason);
                break;
            case self::MSG_TYPE_CONFIG_WARNING:
                $pattern = "「%s」-「%s」-「%s」即将于%d小时后开始，请尽量于每天17点30前完成资源位物料配置和定时。如果资源位开始前1小时未配置，将系统监测并触发大群提醒。";
                $lastTime = strtotime($params['start_show_time'] ?? '') - time();
                $lastTime = floor($lastTime / 3600);
                $msg = vsprintf($pattern, [
                    $params['project_name'] ?? '',
                    $params['ad_type_name'] ?? '',
                    $params['start_show_time'] ?? '',
                    $lastTime,
                ]);
                echo $msg . PHP_EOL;

                $wx_tag_id = $params['wx_tag_id'] ?? '';
                $touser = $params['wx_userid'] ?? '';

                if ($wx_tag_id) {
                    $param = [
                        'totag' => $wx_tag_id,
                        'content' => $msg,
                        'agentid' => WorkWeChat::AGENT_1000066,
                    ];
                } else {
                    $param = [
                        'touser' => $touser,
                        'content' => $msg,
                        'agentid' => WorkWeChat::AGENT_1000066,
                    ];
                }

                if ($touser || $wx_tag_id) {
                    $rs = \Doraemon\tools\Curl::Request('post', WorkWeChat::WECHAT_NOTICE_URL,
                        ['form_params' => $param]);
                    var_dump($param, $rs);
                }
                break;
            default:
                return;
                break;
        }

        if (!empty($markDownContent)) {
//            WorkWeChat::sendGroupRobootMsg(WorkWeChat::AGENT_1000004, WorkWeChat::ROBOT_POSITION_NOTICE,
//                $markDownContent);
        }

    }

    /**
     * 部门选项
     * @return array|bool
     */
    public static function departmentOptions()
    {
        $list = MHhzDepartment::select('*');

        return empty($list) ? [] : array_column($list, 'name', 'id');
    }

    /**
     * 占位类型选项
     * @return array|bool
     */
    public static function positionAdTypeOptions()
    {
        $list = MPositionAdType::select('*');

        return empty($list) ? [] : array_column($list, 'type_name', 'id');
    }

    /**
     * 创建占位db记录
     * @param $id
     * @param $isShare
     * @param $adId
     * @param $adUnique
     * @param $startDate
     * @param $endDate
     * @param $departmentId
     * @param $projectId
     */
    public static function createProjectRelations(
        $id,
        $isShare,
        $adId,
        $adUnique,
        $startDate,
        $endDate,
        $departmentId,
        $projectId,
        $materialId = '',
        $is_exclusive = MPositionProjectRelations::NOT_EXCLUSIVE
    ) {
        if ($id > 0) {
            $data = [
                'department_id' => $departmentId,
                'project_id' => $projectId,
                'ad_id' => $adId,
                'ad_unique' => $adUnique,
                'is_share' => $isShare,
                'start_show_time' => $startDate,
                'end_show_time' => $endDate,
                'edittime' => date("Y-m-d H:i:s"),
                'material_id' => $materialId,
                'is_exclusive' => $is_exclusive
            ];

            MPositionProjectRelations::update($data, ['id' => $id]);

            return;
        }

        $data = [
            'department_id' => $departmentId,
            'project_id' => $projectId,
            'ad_id' => $adId,
            'ad_unique' => $adUnique,
            'is_share' => $isShare,
            'start_show_time' => $startDate,
            'end_show_time' => $endDate,
            'addtime' => date("Y-m-d H:i:s"),
            'material_id' => $materialId,
            'is_exclusive' => $is_exclusive
        ];

        MPositionProjectRelations::insert($data);
    }

    public static function adWarningProject($time)
    {
        $condition = [
            'project_status' => MPositionProject::PROJECT_STATUS_OK,
            'end_show_time[>]' => date("Y-m-d H:i:s"),
        ];
        //查询未开始的项目
        $projectList = MPositionProject::select('*', $condition);
        $projectList = empty($projectList) ? [] : array_column($projectList, null, 'id');

        if (empty($projectList)) return false;
        //查询项目中的占位
        $project_ids = array_column($projectList, 'id');
        $relations = MPositionProjectRelations::select('*', [
            'project_id' => $project_ids,
            'start_show_time[<]' => date("Y-m-d H:i:s", strtotime($time)),
            'start_show_time[>]' => date("Y-m-d H:i:s"),
            'end_show_time[>]' => date("Y-m-d H:i:s"),
            'project_status' => MPositionProjectRelations::PROJECT_STATUS_OK,
        ]);
        $response = [];
        if ($relations) {
            foreach ($relations as $k => $v) {
                $projectInfo = $projectList[$v['project_id']] ?? [];
                if (empty($projectInfo)) continue;
                $v['project'] = $projectInfo;
                $response[] = $v;
            }
        }
        return $response;
    }

    public static function adWarning($time = '1 days')
    {
        $projectRelations = self::adWarningProject($time);
        if (empty($projectRelations)) return false;
        //查询管理员信息
        $admin_info = MHhzAdmin::select('*', ['is_deleted' => 0]);
        $admin_info = empty($admin_info) ? [] : array_column($admin_info, null, 'id');
        //查询资源位名称
        $positions = MPositionAdType::getAllPosition2Cron();
        $positionConfig = empty($positions) ? [] : array_column($positions, 'position_config', 'id');
        $positions = empty($positions) ? [] : array_column($positions, 'type_name', 'id');
        //占位id集合
        $rel_ids = array_column($projectRelations, 'id');
        $exists = MPositionBaseInfo::select('rel_id', ['info_status' => MPositionBaseInfo::STATUS_NORMAL, 'rel_id' => $rel_ids]);
        if ($exists) $rel_ids = array_diff($rel_ids, $exists);

        //物料提醒
        $ignore_ad = [];
        $remind = [];
        $emailCC = self::emailCC($admin_info);

        foreach ($projectRelations as $v) {
            try {
                if (!in_array($v['id'], $rel_ids)) continue;
                $projectInfo = $v['project'];
                $adminInfo = $admin_info[$projectInfo['admin_id']] ?? [];
                $project_name = $projectInfo['project_name'];
                $ad_type_name = $positions[$v['ad_id']] ?? '-';
                $config = $positionConfig[$v['ad_id']] ?? [];
                if ($config) {
                    $config = json_decode($config, true);
                    $module = $config['module_type'] ?? '-1';
                    if ($module != 0) continue;
                }
                $start_show_time = $v['start_show_time'];
                //todo 发送个人消息

                $remind[$project_name][] = [
                    'name' => $ad_type_name,
                    'time' => $start_show_time,
                    'email' => $adminInfo['email'] ?? '',
                    'cc' => $emailCC[$projectInfo['department_id']] ?? [],
                ];

            } catch (\Exception $e) {

            }
        }

        //发送1小时左右提醒邮件
        if ($remind && ($time != '1 days')) {
            foreach ($remind as $project_name => $v) {

                try {
                    $mail = $v[0]['email'];

                    if ($mail) self::sendEmail($mail, $project_name, $v);

                } catch (\Exception $e) {

                }
            }
        }
    }

    public static function emailCC($admin_info)
    {
        $all_admin = empty($admin_info) ? [] : array_column($admin_info, 'email', 'account');
        $config = [
            //产品运营中心 POC - 商家运营组
            5 => ['李想' => 'lixiang@haohaozhu.com'],
            //产品运营中心（POC）-平台运营组
            20 => ['脱瑞卿' => 'tuoruiqing@haohaozhu.com'],
            //产品运营中心（POC）-基础运营组
            21 => ['周超' => 'zhouchao@haohaozhu.com'],
            //产品运营中心（POC）-平台策略组
            22 => ['刘璐' => 'liulu@haohaozhu.com'],
            //产品运营中心（POC）-平台产品组
            23 => ['柳青' => 'liuqing@haohaozhu.com'],
            //市场-活动组
            12 => ['张萌' => 'zhangmeng@haohaozhu.com'],
            //创作者中心（COC）-创作者商业化
            26 => ['黄伊晨' => 'huangyichen@haohaozhu.com'],
            //创作者中心（COC）-创作者运营
            25 => ['南冰' => 'nanbing@haohaozhu.com'],
            //内容中心（CC）-运营组
            8 => ['张亦驰' => 'zhangyichi@haohaozhu.com'],
            //内容中心（CC）-内容组
            7 => ['宋欣' => 'songxin@haohaozhu.com', '张硕' => 'zhangshuo@haohaozhu.com', '王莉雯' => 'jiangbola@haohaozhu.com'],
            //内容中心（CC）-策划组
            14 => ['石书蕴' => 'shishuyun@haohaozhu.com'],
            //品牌营销中心(BMC)-广告项目组
            13 => ['聂晋秋' => 'niejinqiu@haohaozhu.com'],
            //品牌营销中心(BMC)-媒介组
            18 => ['梁静' => 'liangjing@haohaozhu.com'],
            //品牌营销中心(BMC)-广告策划组
            24 => ['戴悦' => 'daiyue@haohaozhu.com'],
            //设计师运营中心(DOC)-商业用户运营
            27 => ['李佳琪' => 'lijiaqi1@haohaozhu.com', '周英龙' => 'zhouyinglong@haohaozhu.com', '韩丽凡' => 'hanlifan@haohaozhu.com'],
            //设计师运营中心(DOC)-交易平台运营
            4 => ['柳林' => 'liulin@haohaozhu.com'],
            //品牌公关
            15 => ['何蕾' => 'helei@haohaozhu.com', '甘丹' => 'gandan@haohaozhu.com', '刘瑞媛' => 'liuruiyuan@haohaozhu.com'],
        ];

        $response = [];
        foreach ($config as $department_id => $list) {
            $temp = [];
            foreach ($list as $name => $email) {
                $temp[] = [
                    'name' => $name,
                    'address' => $email,
                ];
            }
            $response[$department_id] = $temp;
        }

        return $response;
    }

    public static function sendEmail($email, $projectName, $config = [])
    {
        $title = "资源位上传提醒-距离资源位生效仅剩1小时左右";

        $project = '';
        $cc = [];
        foreach ($config as $v) {
            $time = $v['time'];
            $name = $v['name'];
            $project .= <<<PROJECT
<div style="font-family: Arial, Helvetica, sans-serif;">
    <font size="4">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[{$projectName}]项目--{$time}--{$name}，即将于1小时左右后生效，请及时上传资源位素材。</font>
</div>
PROJECT;
            if ($v['cc']) {
                $cc = $v['cc'];
            }
        }

        $body = <<<HTML
<div>
    <span style="font-size: small; color: rgb(34, 34, 34); font-family: Arial, Helvetica, sans-serif;">Dear all</span>
    <b style="font-size: small; color: rgb(34, 34, 34); font-family: Arial, Helvetica, sans-serif;">，</b>
    <span style="color: rgb(34, 34, 34); font-size: small; font-family: Arial, Helvetica, sans-serif;">&nbsp;</span></div>
    <div style="">
        $project
        <div style="font-family: Arial, Helvetica, sans-serif;">
            <font size="4">&nbsp; &nbsp; &nbsp; &nbsp; 上线前请务必使用uid进行测试。</font>
        </div>
        <div style="font-family: Arial, Helvetica, sans-serif;">
            <font size="4">&nbsp; &nbsp; &nbsp; &nbsp; </font>
            <span style="font-size: large;"><font color="#222222">上传完成，请上线后第一时间进行复查</font><font color="#ff0000"><b>。</b></font></span>
        </div>
        <div style="font-family: Arial, Helvetica, sans-serif;">
            <span style="font-size: large;">
                <font color="#ff0000"><b>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;注意：因配置失误和漏传发生的一系列后果，将由项目负责人承担</b></font>
            </span>
        </div>
        <div style="font-family: Arial, Helvetica, sans-serif;">
            <span style="color: rgb(34, 34, 34); font-size: large;"><br></span>
        </div>
        <div style="font-family: Arial, Helvetica, sans-serif;"><br></div>
    </div>
</div>
HTML;

        \Biz\tools\Mail::send($email, $title, $body, null, '', $cc);
    }
}